<?
  //extract the correct parameter value. Convert entity id to entity name...
  function prepareMacro($macro,$parameter) {
    global $db,$ivrPrefix;
    
    $qry="SELECT * FROM cw_macro WHERE id=$macro";
    $resM=$db->Execute($qry);
    $arrM=$resM->FetchRow();
    $macroName=$arrM["name"];

    //extract the parameters
    $params=xml2array($arrM["parameter"]);
      
    $pArr=split("\|",$parameter);
    if (is_array($pArr))
    foreach ($pArr as $v) {
      $keyvalue=split("=",$v);
      $selParamsArr[$keyvalue[0]]=$keyvalue[1];
    } 
    
    $arg="";
    if (is_array($params))
    foreach($params as $p) {
      if ($p["name"]!="param") continue;
      $pName=$p["attributes"]["name"];
      $pType=$p["attributes"]["type"];
      switch ($pType) {
        case "QUEUE":
          $r=$db->Execute("SELECT * FROM cw_queue WHERE id=".$selParamsArr[$pName]);
          $q=$r->FetchRow();
          $arg.="|\"".$q["name"]."\"";
        break;
        case "GROUP":
          $resG=$db->Execute("SELECT * FROM cw_group_entity WHERE group_id=".$selParamsArr[$pName]);
          while ($arrG=$resG->FetchRow()) {
            $r=$db->Execute("SELECT * FROM `cw_channel` WHERE `key`=".$arrG["entity_id"]);
            $a=$r->FetchRow();
            $grp.=$a["name"]."&";
          }
          $grp=substr($grp,0,-1);
          $arg.="|".$grp;
        break;
        case "EXTENSION":
          $r=$db->Execute("SELECT * FROM `cw_channel` WHERE `key`=".$selParamsArr[$pName]);
          $a=$r->FetchRow();
          $arg.="|".$a["name"];
        break;
        case "IVR":
          $context=$ivrPrefix.$selParamsArr[$pName];
          $arg.="|".$context;
        break;
        case "EMAIL_EXTENSION":
        break;
        case "SOUNDFILE":
        case "TEXTFIELD":
        case "TRUNK":
        case "OUTGOING_RULE":
          $arg.="|\"".varEval($selParamsArr[$pName])."\"";
        break;
        default:
          agiError("ERROR: unknown macro parameter type \"".$pType."\"");
        break;
      }
    }
      
    return array($macroName,$arg);      
  }
  
  //search for some variable and evaluate it
  function varEval($txt) {
    global $agi;
    if (substr(trim($txt),0,2)=="\${" and $agi!=null) {
      $ret=$agi->get_variable(substr(trim($txt),2,-1));
      $txt=$ret["data"];
    }
    return $txt;
  }

  function _getGroup($entity_id) {
    global $db;
    if ($entity_id=="") return 0;
    $qry="SELECT * FROM cw_group_entity WHERE entity_id=$entity_id";
    $res=$db->Execute($qry);
    while ($arr=$res->FetchRow()) $ret[]=$arr["group_id"];
    return $ret;
  }

  function _getTrunk($r) {
    global $db;
    $ret="";
    $arr=split("\|",$r);
    if (is_array($arr)) 
    foreach ($arr as $t) {
      $qry="SELECT * FROM cw_channel WHERE id=$t";
      $res=$db->Execute($qry);
      $arr=$res->FetchRow();
      $ret.=$arr["name"]."&";
    }
    if($ret!="") $ret=substr($ret,0,-1);
    return $ret;
  }

  function _extractTech($entity_id) {
    global $db;
    $qry="SELECT * FROM cw_entity WHERE id=$entity_id";
    $res=$db->Execute($qry);
    $arr=$res->FetchRow();
    $type=$arr["type"];
    switch ($type) {
      case EXTENSION:
        $qry="SELECT t.name as techName FROM cw_client c INNER JOIN cw_technology t ON c.technology=t.id WHERE c.id=$entity_id";
        $resTech=$db->Execute($qry);
        $clientArr=$resTech->FetchRow();
        $techName=$clientArr["techName"];
        $ret=$techName;
        break;
/*
      case TRUNK: 
        $qry="SELECT * FROM cw_trunk WHERE id=$entity_id";
        $res=$db->Execute($qry);
        $arr=$res->FetchRow();
        $ret=$arr["name"];
        break;       
*/
    }
    return $ret;
  }
  
  function checkTime($ts=0) {
    global $db;
    
    if ($ts==0) {
      return true;
    }      
    
    //check the time segment
    $qry="SELECT * FROM cw_time_segment WHERE id=$ts";
    $res=$db->Execute($qry);
    $arrTs=$res->FetchRow();

    $id=$arrTs["id"];
    $name=$arrTs["name"];
    $date_from=$arrTs["date_from"];
    $date_to=$arrTs["date_to"];
    $hour_from=$arrTs["hour_from"];
    $hour_to=$arrTs["hour_to"];
    $week_day=$arrTs["week_day"];
    $day=$arrTs["day"];
    $month=$arrTs["month"];  
    
    if ($date_from != "") $qry=$qry." AND NOW()>=$date_from";

    if ($date_to != "") $qry=$qry." AND NOW()<=$date_to";

    if ($hour_from != "") {
      $qryTmp="";
      $hour_fromArr=split("\|",$hour_from);
      $hour_toArr=split("\|",$hour_to);
      foreach ($hour_fromArr as $i=>$h) {
        if ($h=="") continue;
        $h=extractTime($h);
        $h2=extractTime($hour_toArr[$i]);
        $qryTmp=$qryTmp." OR (TIME(NOW())>=$h AND TIME(NOW())<=$h2) ";
      }
      $qry.=" AND (".substr($qryTmp,3).")";
    }


    if ($week_day != "") {
      $qryTmp="";
      $week_dayArr=split("\|",$week_day);
      foreach ($week_dayArr as $w) {
        $qryTmp.=" OR DAYOFWEEK(NOW())='$w'";
      }
      $qry.=" AND (".substr($qryTmp,3).")";
    }

    if ($day != "") {
      $qryTmp="";
      $dayArr=split("\|",$day);
      foreach ($dayArr as $d) {
        $qryTmp.=" OR DAYOFMONTH(NOW())='$d'";
      }
      $qry.=" AND (".substr($qryTmp,3).")";
    } 

    if ($month != "") {
      $qryTmp="";
      $monthArr=split("\|",$month);
      foreach ($monthArr as $m) {
        $qryTmp.=" OR MONTH(NOW())='$m'";
      }    
      $qry.=" AND (".substr($qryTmp,3).")";
    } 

    $res=$db->Execute($qry);
    if ($res->RecordCount()==0) {
      agiError("  TimeSegment return false");
      return false;
    } else {
      agiError("  TimeSegment return true");
      return true;
    }
  }

  if (!function_exists("errArr")) {
  function errArr($arr) {
    if (count($arr)==0) agiError("[No item in this array]");
    else {
      ob_start();
        print_r($arr);
      $contents=ob_get_contents();
      ob_end_clean();
      agiError($contents);     
    }
    unset($arr);
    return;
  }
  }
  
  if (!function_exists("xml2array")) {
  function xml2array ($xml)
  {
     $xmlary = array ();
  
     if ((strlen ($xml) < 256) && is_file ($xml))
       $xml = file_get_contents ($xml);
    
     $ReElements = '/<(\w+)\s*([^\/>]*)\s*(?:\/>|>(.*?)<(\/\s*\1\s*)>)/s';
     $ReAttributes = '/(\w+)=(?:"|\')([^"\']*)(:?"|\')/';
    
     preg_match_all ($ReElements, $xml, $elements);
     foreach ($elements[1] as $ie => $xx) {
     $xmlary[$ie]["name"] = $elements[1][$ie];
       if ( $attributes = trim($elements[2][$ie])) {
           preg_match_all ($ReAttributes, $attributes, $att);
           foreach ($att[1] as $ia => $xx)
             // all the attributes for current element are added here
             $xmlary[$ie]["attributes"][$att[1][$ia]] = $att[2][$ia];
       } // if $attributes
       
       // get text if it's combined with sub elements
     $cdend = strpos($elements[3][$ie],"<");
     if ($cdend > 0) {
             $xmlary[$ie]["text"] = substr($elements[3][$ie],0,$cdend -1);
         } // if cdend
         
       if (preg_match ($ReElements, $elements[3][$ie])){        
           $xmlary[$ie]["elements"] = xml2array ($elements[3][$ie]);
           }
       else if (isset($elements[3][$ie])){
           $xmlary[$ie]["text"] = $elements[3][$ie];
           }
       $xmlary[$ie]["closetag"] = $elements[4][$ie];
     }//foreach ?
     return $xmlary;
  } 
  }
    
function extractTime($t) {
  $t=str_replace(",",":",$t);
  $t=str_replace(".",":",$t);
  
  $arr=split(":",$t);
  for ($i=0; $i<3; $i++) {
    if (isset($arr[$i])) $arg.=$arr[$i].",";
    else $arg.="0,";
  }
  $ret="MAKETIME(".substr($arg,0,-1).")";
  return $ret;  
}  

function _getDiffChannel($permitted,$denied){
  $permittedArr=split("&",$permitted);  
  $deniedArr=split("&",$denied);  
  $diff=array_diff($permittedArr,$deniedArr);
  $ret="";
  foreach($diff as $ch){
    $ret.=$ch."&";
  }     
  $ret=substr($ret, 0, -1);
  return $ret;
}

function _matchPattern($ext,$pattern){
  $debug=false;
  // pattern is a match case (return false if different)
  if($pattern[0]!='_'){
    if($pattern==$ext) return true;
    else return false;
  }
  // pattern is a regular expression (return false at the first difference)
  if($debug===true)agiError("(Pattern) $pattern <==> $ext (Number)");
  $pattern=substr($pattern,1);  // remove "_"  
  $pat_pos=0;
  $ext_pos=0;
  while($pat_pos<strlen($pattern)){
    switch($pattern[$pat_pos]){
        case "0":
        case "1":
        case "2":
        case "3":
        case "4":
        case "5":
        case "6":
        case "7":
        case "8":
        case "9":
          if($pattern[$pat_pos]!=$ext[$ext_pos]) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break;
        case "X":
          if(($ext[$ext_pos]=="")||(!(in_array($ext[$ext_pos],range(0,9))))) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break;
        case "Z":
          if(($ext[$ext_pos]=="")||(!(in_array($ext[$ext_pos],range(1,9))))) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break;
        case "N":
          if(($ext[$ext_pos]=="")||(!(in_array($ext[$ext_pos],range(2,9))))) return false;
          if($debug===true) agiError("    |> ".$pattern[$pat_pos]." == ".$ext[$ext_pos]); 
          $pat_pos++;
          $ext_pos++;
          break; 
        case "[":
          if($debug===true) agiError("    |> \"[\""); 
          $remaining=substr($pattern,$pat_pos);
          $end_pos=strpos($remaining,"]");
          if($end_pos===false){
            agiError("PARSING ERROR: check pattern definition ($pattern)");
            return false;
          }
          $p_content=substr($remaining,1,($end_pos)); 
          $p_found=false;
          $p_end=false;
          $j=0;
          while(($j<(strlen($p_content)-1))&&($p_found===false)){
            $next=$j+1;
            switch($p_content[$next]){
              case "0":
              case "1":
              case "2":
              case "3":
              case "4":
              case "5":
              case "6":
              case "7":
              case "8":
              case "9":
                if($ext[$ext_pos]==$p_content[$j]) $p_found=true;
                if($debug===true) agiError("       |> ".$ext[$ext_pos]." == ".$p_content[$j]);
                $j++;
                break;
              case "]":
                if($ext[$ext_pos]==$p_content[$j]) $p_found=true;
                $p_end=true;
                if($debug===true) agiError("       |> ".$ext[$ext_pos]." == ".$p_content[$j]);
                $j++;
                break;
              case "-":
                $range_start=$j;
                $range_end=($j+2);
                if(in_array($ext[$ext_pos],range($p_content[$range_start],$p_content[$range_end]))) $p_found=true;
                if($p_content[($j+3)]=="]") $p_end=true;
                if($debug===true) agiError("       |> ".$ext[$ext_pos]." in [".$p_content[$range_start]."-".$p_content[$range_end]."]");
                $j=($j+3); 
                break;
              default:
                agiError("PARSING ERROR: check pattern definition ($pattern)");
                return false;
                break;
            }
            if($p_end===true) break;
          }
          if($p_found===false) return false;
          $pat_pos=($pat_pos+(strlen($p_content))+1);
          $ext_pos++;
          if($debug===true) agiError("    |> \"]\""); 
          break;  
        case ".":
          if($ext[$ext_pos]=="") return false;
          if($debug===true) agiError("    |> \".\""); 
          return true;
          break;
        default:
          agiError("PARSING ERROR: check pattern definition ($pattern)");
          return false;
          break;
     }
  }
  if($debug===true) agiError("EXIT: if  ".strlen($ext)." == ".$ext_pos."  --> MATCH"); 
  if(strlen($ext)==$ext_pos) return true;
  return false;
}

function _metaGroupFromGroup($group_id) {
  global $db;
  $qry="SELECT * FROM `cw_group` WHERE `id`=".$group_id;
  $res=$db->Execute($qry);
  while ($arr=$res->FetchRow()) { 
    $ret=$arr["type"];
  }
  return $ret;
}

function _isIncluded($extension,$entity,$group,$output=true){
  global $db;
  $included=false;
  if($entity > 0){ // class of number
    $qry="SELECT * FROM cw_number WHERE id=".$entity;
    $numbers=$db->Execute($qry);
    while($row=$numbers->FetchRow()){
      $patternArr=split("\|",$row["number"]);
      foreach($patternArr as $pattern){
        $included=_matchPattern($extension,$pattern);
        if($included===true) {
          if($output===true) agiError("    --> search for pattern $pattern : ! MATCH !");
          break;       
        }else if($output===true) agiError("    --> search for pattern $pattern : NO MATCH");
      }
    }
  }else if($group > 0) { // group of class of numbers
    if(($group!=1)&&($group!=GROUP_OF_PHONENUMBER)){ 
      $qry="SELECT * FROM `cw_group_entity` WHERE `group_id`=".$group;
      $classes=$db->Execute($qry);
      while($row=$classes->FetchRow()){
        $qry="SELECT * FROM cw_number WHERE id=".$row["entity_id"];
        $numbers=$db->Execute($qry);
        while($row=$numbers->FetchRow()){
          $patternArr=split("\|",$row["number"]);
          foreach($patternArr as $pattern){
            $included=_matchPattern($extension,$pattern);
            if($included===true) {
              if($output===true) agiError("    --> search for pattern $pattern : ! MATCH !");
              break;       
            }else if($output===true) agiError("    --> search for pattern $pattern : NO MATCH");
          }
        }
        if($included===true) break;  
      }
    }else{ //$group==1 --> destination * || group==5 --> destination ALL_PHONENUMBERS
      $included=true; 
      if($output===true) agiError("    --> match for all pattern");
    }
  }
  return $included;
}

function _isPrevDenied($extension,$ruleSetId,$priority){
  global $db;
  $deniedTrunks="";
  $deniedOnRules="";
  $deniedAll=false;
  // retrieve previous denies
  $qry="SELECT r.* , g.type , ge.`group_id`";
  $qry.=" FROM (cw_rule_outgoing AS r LEFT JOIN cw_group AS g ON r.`group` = g.`id`)";
  $qry.=" LEFT JOIN cw_group_entity AS ge ON r.`entity` = ge.`entity_id`";
  $qry.=" WHERE rule=".$ruleSetId."";
  $qry.=" AND r.`priority`<".$priority." AND r.`action`=".RULE_DENY."";
  $qry.=" AND ( r.`group`=".GROUP_OF_ALL." OR g.`type`=".GROUP_OF_PHONENUMBER." OR ge.`group_id`=".GROUP_OF_PHONENUMBER." )";
  $qry.=" ORDER BY r.`priority` DESC";
  $prev_rules=$db->Execute($qry);
  // analyse rules
  while ($rule=$prev_rules->FetchRow()) {
    $id=$rule["id"];
    $entity=$rule["entity"];
    $group=$rule["group"];
    $resurces=$rule["resource"];
    $timesegment=$rule["timesegment"];
    $deniedAll=false;
    // check time segment
    if(checkTime($prev_timesegment)===false) continue;
    // check resurces
    switch ($group){
      // (All) or (All phone number)
      case GROUP_OF_ALL:          
      case GROUP_OF_PHONENUMBER: 
        if($resurces!="0"){
          $deniedTrunks.=_getTrunk($resurces)."&";
          $deniedOnRules.=$id." ";
        }else $deniedAll=true;   
      break;
      // (Class of numbers) or (Group of class of numbers)
      case 0:   
      default: 
        $included=_isIncluded($extension,$entity,$group,false);
        if($included===true){
          if($resurces!="0"){
            $deniedTrunks.=_getTrunk($resurces)."&";
            $deniedOnRules.=$id." ";
          }else $deniedAll=true;
        }
      break;  
    }
    if($deniedAll===true) break; 
  }
  if($deniedAll===true){
    $ret["all"]=true;
    $ret["list"]="";
    agiError("  Previously has been denied all");
  }else{
    if($deniedTrunks!="") $deniedTrunks=substr($deniedTrunks,0,-1);
    $ret["all"]=false;
    $ret["list"]=$deniedTrunks;
    if($deniedOnRules!=""){
      agiError("  Previously has been denied: ");
      agiError("    > on rules:  ".$deniedOnRules);
      agiError("    > on trunks: ".$deniedTrunks);    
    }
    else agiError("  Previously has been never denied");
  }
  return $ret;                  
}

function agiError($string){
  global $agiDebug;
  if($agiDebug===true){ 
    error_log($string);
    return 1;
  }
  else return 0; 
}
  
?>
